package com.jlf.core.engine.builder;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

import java.io.File;
import java.io.FileOutputStream;


/**
 * dao的类自动构建
 *
 * @author wujr
 * 2023/7/7
 * @version 1.0
 * 【变更历史】
 * [wujr 2023/7/7 1.0 新增]
 */
@Slf4j
@Getter
public class AutoDaoBuilder {
    private final boolean saveDaoClass = false;
    private final AutoDomainBuilder domainBuilder;
    private String asmDaoInternal;
    private Class<?> daoClass;
    private String daoType;

    public AutoDaoBuilder(AutoDomainBuilder domainBuilder){
        this.domainBuilder = domainBuilder;
    }
    /**
     * 构建模型领域的dao接口
     * 本方法采用asm
     * @return 模型的dao接口
     * @throws Exception 构建失败，抛出异常
     */
    public Class<?> generate() throws Exception{
        String daoClass = this.domainBuilder.getBasePackage() + ".dao." + this.domainBuilder.getDomainSimpleCode() + "Dao";
        try{
            this.daoClass = this.domainBuilder.getEngineClassLoader().loadClass(daoClass);
            this.asmDaoInternal = Type.getInternalName(this.daoClass);
            this.daoType = Type.getDescriptor(this.daoClass);
            return this.daoClass;
        }catch (Exception e){
            log.trace("加载模型领域：{} 的dao，该mapper不存在，需要动态创建", this.domainBuilder.getDomainSimpleCode());
        }
        this.asmDaoInternal = AutoBuilderTool.class2Asm(daoClass);
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
        String signature = AutoBuilderTool.asmSimpleDaoSignature("L" + this.domainBuilder.getAsmDomainInternal() + ";");
        cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT, asmDaoInternal, signature, "java/lang/Object", new String[]{AutoBuilderTool._asmSimpleDaoInternal});
        cw.visitEnd();
        byte[] code = cw.toByteArray();
        if (saveDaoClass){
            String tempPath = System.getProperty("java.io.tmpdir") + File.separator;
            FileOutputStream fos = new FileOutputStream(tempPath + this.domainBuilder.getDomainSimpleCode() + "Dao.class");
            fos.write(code);
            fos.close();
        }
        this.daoClass = this.domainBuilder.getEngineClassLoader().defineClass(daoClass, code);
        this.daoType = Type.getDescriptor(this.daoClass);
        return this.daoClass;
    }
}
